package media

import (
    "bytes"
    "fmt"
    //"time"
    "io"
    "os/exec"
    "os"

    "log"
    "sync"

    "github.com/pion/rtp"
    "github.com/bluenviron/gortsplib/v4"
    "github.com/bluenviron/gortsplib/v4/pkg/base"
    "github.com/bluenviron/gortsplib/v4/pkg/description"
    "github.com/bluenviron/gortsplib/v4/pkg/format"
    
)

// 定义一个结构体来存储每个设备的 ffmpeg 进程和输入管道
type DeviceStream struct {
	stdinPipe io.WriteCloser // 标准输入管道
	cmd       *exec.Cmd
}

// 全局的设备流映射，用于存储每个设备的流信息
var deviceStreams = make(map[string]*DeviceStream)
var mu sync.Mutex

var file *os.File

// 启动 ffmpeg 进程
func startFFMPEG(deviceID string) error {
	mu.Lock()
	defer mu.Unlock()

	// 检查设备是否已经启动推流
	if _, exists := deviceStreams[deviceID]; exists {
		return fmt.Errorf("device %s is already streaming", deviceID)
	}

	//rtspURL := "rtsp://192.168.1.213:8554/" + deviceID
	// cmd := exec.Command(
	// 	"ffmpeg",
	// 	"-i", "pipe:0",            // 从标准输入读取数据
	// 	"-c", "copy",         // 直接复制流，不重新编码
	// 	"-f", "rtsp",         // 输出格式为 RTSP
	// 	rtspURL,              // RTSP 服务器地址
	// )

	//-f flv rtmp://192.168.1.213:1935/live/34020000001320000001
	rtspURL := "rtmp://127.0.0.1:1935/live/" + deviceID
	cmd := exec.Command(
		"ffmpeg",
		"-i", "pipe:0",            // 从标准输入读取数据
		"-c:v", "libx264",         // 直接复制流，不重新编码
		"-preset", "medium",
		"-tune", "zerolatency",
		"-c:a", "copy",
		"-f", "flv",         // 输出格式为 RTSP
		rtspURL,              // RTSP 服务器地址
	)

	// 创建标准输入管道
	stdinPipe, err := cmd.StdinPipe()
	if err != nil {
		return fmt.Errorf("error creating stdin pipe for device %s: %v", deviceID, err)
	}

    file, err = os.Create("output.h264")
	if err != nil {
		fmt.Println("Error creating H.264 file:", err)
	}
	//defer file.Close()

	// 捕获 ffmpeg 的输出（用于调试）
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Stderr = &out

	// 启动 ffmpeg
	err = cmd.Start()
	if err != nil {
		return fmt.Errorf("error starting ffmpeg for device %s: %v", deviceID, err)
	}

	// 将设备的流信息存储到映射中
	deviceStreams[deviceID] = &DeviceStream{
		stdinPipe: stdinPipe,
		cmd:       cmd,
	}
    
	// 等待 ffmpeg 进程结束，并获取错误信息
	go func() {
		err := cmd.Wait()
		if err != nil {
			fmt.Printf("FFmpeg process for device %s exited with error: %v\n", deviceID, err)
			fmt.Printf("FFmpeg output: %s\n", out.String())
		} else {
			fmt.Printf("FFmpeg process for device %s exited successfully\n", deviceID)
		}
		// 从映射中移除设备的流信息
		mu.Lock()
		delete(deviceStreams, deviceID)
		mu.Unlock()
	}()

	return nil
}

// 推送 H.264 数据到 FFmpeg 的标准输入
func pushH264Data(deviceID string, h264Data []byte) error {
	mu.Lock()
	defer mu.Unlock()

	// 检查设备是否已经启动推流
	stream, exists := deviceStreams[deviceID]
	if !exists {
		return fmt.Errorf("device %s is not streaming", deviceID)
	}

	// 检查数据是否包含起始码
	//if len(h264Data) < 4 || (h264Data[0] != 0x00 || h264Data[1] != 0x00 || h264Data[2] != 0x00 || h264Data[3] != 0x01) {
		//fmt.Printf("Warning: H.264 data for device %s does not start with start code\n", deviceID)
	//}
    bufferMutex.Lock()
	// 将 H.264 数据写入 FFmpeg 的标准输入管道
	_, err := stream.stdinPipe.Write(h264Data)
	if err != nil {
        bufferMutex.Unlock()
        fmt.Println("error writing data to ffmpeg stdin for device %s ", deviceID )
		return fmt.Errorf("error writing data to ffmpeg stdin for device %s: %v", deviceID, err)
	}
    //  _, err = file.Write(h264Data)
    // if err != nil {
    //      fmt.Println("Error writing to H.264 file:", err)
    // }
	stream.stdinPipe.(*os.File).Sync()
    bufferMutex.Unlock()

	return nil
}

// 停止 ffmpeg 进程
func stopFFMPEG(deviceID string) error {
	mu.Lock()
	defer mu.Unlock()

	// 检查设备是否已经启动推流
	stream, exists := deviceStreams[deviceID]
	if !exists {
		return fmt.Errorf("device %s is not streaming", deviceID)
	}

	// 关闭标准输入管道
	err := stream.stdinPipe.Close()
	if err != nil {
		return fmt.Errorf("error closing stdin pipe for device %s: %v", deviceID, err)
	}

	// 等待 ffmpeg 完成
	err = stream.cmd.Wait()
	if err != nil {
		return fmt.Errorf("error waiting for ffmpeg process for device %s: %v", deviceID, err)
	}

	// 从映射中移除设备信息
	delete(deviceStreams, deviceID)

	fmt.Println("Stream pushed to RTSP server successfully for device", deviceID)
	return nil
}

// This example shows how to
// 1. create a RTSP server which accepts plain connections
// 2. allow a single client to publish a stream with TCP or UDP
// 3. allow multiple clients to read that stream with TCP, UDP or UDP-multicast

type serverHandler struct {
	s         *gortsplib.Server
	mutex     sync.Mutex
	stream    *gortsplib.ServerStream
	publisher *gortsplib.ServerSession
}

// called when a connection is opened.
func (sh *serverHandler) OnConnOpen(ctx *gortsplib.ServerHandlerOnConnOpenCtx) {
	log.Printf("conn opened")
}

// called when a connection is closed.
func (sh *serverHandler) OnConnClose(ctx *gortsplib.ServerHandlerOnConnCloseCtx) {
	log.Printf("conn closed (%v)", ctx.Error)
}

// called when a session is opened.
func (sh *serverHandler) OnSessionOpen(ctx *gortsplib.ServerHandlerOnSessionOpenCtx) {
	log.Printf("session opened")
}

// called when a session is closed.
func (sh *serverHandler) OnSessionClose(ctx *gortsplib.ServerHandlerOnSessionCloseCtx) {
	log.Printf("session closed")

	sh.mutex.Lock()
	defer sh.mutex.Unlock()

	// if the session is the publisher,
	// close the stream and disconnect any reader.
	if sh.stream != nil && ctx.Session == sh.publisher {
		sh.stream.Close()
		sh.stream = nil
	}
}

// called when receiving a DESCRIBE request.
func (sh *serverHandler) OnDescribe(ctx *gortsplib.ServerHandlerOnDescribeCtx) (*base.Response, *gortsplib.ServerStream, error) {
	log.Printf("describe request")

	sh.mutex.Lock()
	defer sh.mutex.Unlock()

	// no one is publishing yet
	if sh.stream == nil {
		return &base.Response{
			StatusCode: base.StatusNotFound,
		}, nil, nil
	}

	// send medias that are being published to the client
	return &base.Response{
		StatusCode: base.StatusOK,
	}, sh.stream, nil
}

// called when receiving an ANNOUNCE request.
func (sh *serverHandler) OnAnnounce(ctx *gortsplib.ServerHandlerOnAnnounceCtx) (*base.Response, error) {
	log.Printf("announce request")

	sh.mutex.Lock()
	defer sh.mutex.Unlock()

	// disconnect existing publisher
	if sh.stream != nil {
		sh.stream.Close()
		sh.publisher.Close()
	}

	// create the stream and save the publisher
	sh.stream = gortsplib.NewServerStream(sh.s, ctx.Description)
	sh.publisher = ctx.Session

	return &base.Response{
		StatusCode: base.StatusOK,
	}, nil
}

// called when receiving a SETUP request.
func (sh *serverHandler) OnSetup(ctx *gortsplib.ServerHandlerOnSetupCtx) (*base.Response, *gortsplib.ServerStream, error) {
	log.Printf("setup request")

	// no one is publishing yet
	if sh.stream == nil {
		return &base.Response{
			StatusCode: base.StatusNotFound,
		}, nil, nil
	}

	return &base.Response{
		StatusCode: base.StatusOK,
	}, sh.stream, nil
}

// called when receiving a PLAY request.
func (sh *serverHandler) OnPlay(ctx *gortsplib.ServerHandlerOnPlayCtx) (*base.Response, error) {
	log.Printf("play request")

	return &base.Response{
		StatusCode: base.StatusOK,
	}, nil
}

// called when receiving a RECORD request.
func (sh *serverHandler) OnRecord(ctx *gortsplib.ServerHandlerOnRecordCtx) (*base.Response, error) {
	log.Printf("record request")

	// called when receiving a RTP packet
	ctx.Session.OnPacketRTPAny(func(medi *description.Media, forma format.Format, pkt *rtp.Packet) {
		// route the RTP packet to all readers
		sh.stream.WritePacketRTP(medi, pkt)
	})

	return &base.Response{
		StatusCode: base.StatusOK,
	}, nil
}

func StartRTSP() {
	// configure the server
	h := &serverHandler{}
	h.s = &gortsplib.Server{
		Handler:           h,
		RTSPAddress:       ":8554",
		UDPRTPAddress:     ":8000",
		UDPRTCPAddress:    ":8001",
		MulticastIPRange:  "224.1.0.0/16",
		MulticastRTPPort:  8002,
		MulticastRTCPPort: 8003,
	}

	// start server and wait until a fatal error
	log.Printf("server is ready")
	panic(h.s.StartAndWait())
}
